from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from datetime import datetime
from base64 import decodebytes, encodebytes
from urllib.parse import quote_plus
import json


class Alipay(object):
    def __init__(self, appid, return_url, notify_url, app_private_key_path, alipay_public_key_path):
        self.appid = appid,
        self.return_url = return_url,
        self.notify_url = notify_url,
        self.app_private_key_path = app_private_key_path,
        self.alipay_public_key_path = alipay_public_key_path,
        with open(self.app_private_key_path) as fp:
            self.app_private_key = RSA.importKey(fp.read())
        with open(self.alipay_public_key_path) as fp:
            self.alipay_public_key = RSA.importKey(fp.read())

    def public_parameters(self, method, biz_content, return_url=None):
        # 形成一个字典返回回去
        data = {
            "app_id": self.appid,
            "method": method,
            "charset": "utf-8",
            "sign_type": "RSA2",
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "version": "1.0",
            "biz_content": biz_content
        }
        if return_url is not None:
            data["return_url"] = self.return_url
            data["notify_url"] = self.notify_url
        return data

    def required_parameter(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
        required_data = {
            "subject": subject,
            "out_trade_no": out_trade_no,
            "total_amount": total_amount,
            "product_code": "FAST_INSTANT_TRADE_PAY"
        }
        required_data.update(kwargs)
        data = self.public_parameters("alipay.trade.page.pay", required_data, self.return_url)
        return self.sign_data(data)

    def sign_data(self, data):
        data.pop("sign", None)
        unsigned_items = self.ordered_data(data)
        unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
        sign = self.sign(unsigned_string.encode("utf-8"))
        quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_string)
        signed_string = quoted_string + "&sign=" + quote_plus(sign)
        return signed_string

    # 传入数据开始签名
    def ordered_data(self, data):
        data.pop('sign', None)
        biz_zip = []
        for key, value in data.items():
            if isinstance(value, dict):
                biz_zip.append(key)
        # 得到请求参数的字典添加到data里面，separators 是（ ‘元素之间用逗号隔开’ ， ‘key和内容之间’ 用冒号隔开）
        for key in biz_zip:
            data[key] = json.dumps(data[key], separators=(',', ':'))
        return sorted([(k, v) for k, v in data.items()])

    def sign(self, unsigned_strings):
        # 得到应用私钥
        key = self.app_private_key
        signer = PKCS1_v1_5.new(key)
        signature = signer.sign(SHA256.new(unsigned_strings))
        sign = encodebytes(signature).decode("utf8").replace("\n", "")
        return sign
